using System;
using System.IO;

namespace AntsMap
{
	/// <summary>
	/// Summary description for PheroMap
	/// </summary>
	public class PheroMap
	{
		private double[,] pheromones;
		private int elements;
        private double evaprate; // HAS-QAP param for evaporation rate
        private double alpha2;  // HAS-QAP param for intensify action
        private int offset;
        private Random randy;

        // ele is number of elements in this pheromone map
        // offset corresponds to the dimensions of the original solution
        // an offset of 0 means it's the pheromap for the first dimension,
        // an offset of 1 means it's for the second dimension.  The 3rd doesn't need one (in 3dap)
		public PheroMap( int ele, int offset, double evap, double reinforce )
		{
            randy = new Random( offset ); // seed it with the offset.  Should keep random sequences different.
            elements = ele;
            pheromones = new double[elements, elements];
            evaprate = evap;
            alpha2 = reinforce; // HAS QAP parameter
            
            //   0  1   2
            //  [  |  |  ]
            //  [  |  |  ]
            //  [  |  |  ]
            this.offset = offset; // a value of zero means this is the first phero map (for first column)
        
            // Set the pheromone assignments to zero by calling Diversify()
            this.Diversify();
        }

        // --------------------------------------------------------------------------------------
        // Initialize the pheromone map to a level related to best solution so far
        // --------------------------------------------------------------------------------------
        public void Initialize(double level)
        {
            for (int i = 0; i < elements; i++)
            {
                for (int j = 0; j < elements; j++)
                {
                    pheromones[i, j] = level;
                }
            }
        }

        // --------------------------------------------------------------------------------------
        // Back map - Get the "physical" index that corresponds to the soln index
        // The IndexByPolicyXXXX functions return the actual row index, but the solution
        // matrix may have that row stored in a different index due to how the solutions are stored
        // eg, 222  <-- "physical" index 0, but corresponds to index 2 in solution arrangement
        //     101
        //     ...      (I don't know why I made it this way a few months ago...)
        // --------------------------------------------------------------------------------------
        public int MapItBack(int index, int[,] soln, int elems)
        {
            int mapped = 0;

            for (int i = 0; i < elems; i++)
            {
                if (index == soln[i, this.offset])
                {
                    mapped = i;
                    break;
                }
            }
            return mapped;
        }
        // --------------------------------------------------------------------------------------
        //
        // --------------------------------------------------------------------------------------
        public int IndexByPolicyMaximizeSum(int row, int col, int[,] soln, int numelems)
        {
            double curmax = 0.0; // we are maximizing phero[row,col]+curmax, which is the same as max curmax
            int bestindex = 0;
            int iter = 0;
            for (iter = 0; iter < numelems; iter++)
            {
                // starting at row 0 of the solution, look at all corressponding
                // pheromone entries (except the one at (row,col)) and reset curmax
                // if a greater amount of pheromone is found.
                if (soln[iter,this.offset] != row)
                {
                    int pherow = soln[iter, 0 + this.offset];
                    int phecol = soln[iter, 1 + this.offset]; //if offset is 1, looks at (x,2), eg
                    if (this.pheromones[pherow, phecol] >= curmax)
                    {
                        bestindex = pherow;
                        curmax = this.pheromones[pherow, phecol];
                    }
                }
            }
            /* this should NEVER happen */
            if (row == bestindex)
            {
                //System.Windows.Forms.MessageBox.Show("Max: row is " + row.ToString() + "  and bestindex is " + bestindex.ToString());
                //System.Windows.Forms.Application.Exit();
            }
            return MapItBack(bestindex,soln,numelems);
        }


        // --------------------------------------------------------------------------------------
        //
        // --------------------------------------------------------------------------------------
        public int IndexByPolicyProbability(int row, int col, int[,] soln, int numelems)
        {
            int bestindex = 0;
            int iter = 0;

            int xiter = 0;
            int[] indexmap = new int[numelems - 1];
            double[] phermap = new double[numelems - 1];
            double basepher = this.pheromones[row, col];
            double totalpher = 0.0;

            // Loop through all available indices and calculate the total amnt of pheromone deposits
            for (iter = 0; iter < numelems; iter++)
            {
                if( soln[iter,this.offset] != row )
                {
                    int pherow = soln[iter, 0 + this.offset];
                    int phecol = soln[iter, 1 + this.offset]; //if offset is 1, looks at (x,2), eg
                    totalpher += this.pheromones[pherow, phecol]; 
                }
            }
            
            // build the cumulative distribution for pheromone deposits
            for (iter = 0; iter < numelems; iter++)
            {
                if (soln[iter,offset] != row)
                {
                    // we are considering e-1 indexes (one is already chosen)
                    indexmap[xiter] = soln[iter,offset]; // store the free index
                    int pr, pc = 0;

                    if (xiter == 0)
                    {
                        pr = soln[iter, 0 + this.offset];
                        pc = soln[iter, 1 + this.offset]; //if offset is 1, looks at (x,2), eg
                        phermap[xiter] = ((this.pheromones[pr, pc]) / totalpher);
                    }
                    else
                    {
                        pr = soln[iter, 0 + this.offset];
                        pc = soln[iter, 1 + this.offset]; //if offset is 1, looks at (x,2), eg
                        phermap[xiter] = ((this.pheromones[pr, pc]) / totalpher) + phermap[xiter - 1];
                    }
                    xiter++;
                }
            }

            // Due to rounding errors, the last entry of the CDF in phermap may be 0.9999999999986
            // phermap has elements-1 spaces, starting at 0 so get to the last one via numelems-2
            phermap[numelems - 2] = 1.0;  //Math.Ceiling(phermap[numelems - 2]);

            // Now determine probabalistically which index to return
            double p = randy.NextDouble();
            for (iter = 0; iter < phermap.Length; iter++)
            {
                if (p < phermap[iter])
                {
                    bestindex = indexmap[iter];
                    break;
                }
            }

            /* this should NEVER happen */
            if (row == bestindex)
            {
                //System.Windows.Forms.MessageBox.Show("Prob: row is " + row.ToString() + "  and bestindex is " + bestindex.ToString());
                //System.Windows.Forms.Application.Exit();
            }
            int j = offset;
            return MapItBack(bestindex,soln,numelems);
        }








        // --------------------------------------------------------------------------------------
        // Erase the pheromone trail.
        // --------------------------------------------------------------------------------------
        public void Diversify()
        {
            for (int i = 0; i < elements; i++)
            {
                for (int j = 0; j < elements; j++)
                {
                    pheromones[i, j] = 0.0;
                }
            }
        }


        // --------------------------------------------------------------------------------------
        // Based on the best solution so far, strengthen the pheromone trail
        // (Tij = Tij + a2/f(pistar) for all i
        // --------------------------------------------------------------------------------------
        public void Intensify( int[,] soln, double best )
        {
            double deposit = this.alpha2 / best; // the amount of re-inforcing pheromone to deposit
            
            for (int i = 0; i < this.elements; i++)
            {       
                int row = soln[i, this.offset]; // eg, row 0 of soln may have a 3 in it, meaning 3rd row
                int col = soln[i, this.offset + 1];

                // strengthen the entry for this linkage
                this.pheromones[row, col] += deposit;
            }

        }


        // --------------------------------------------------------------------------------------
        //
        // --------------------------------------------------------------------------------------
        public void Evaporate()
        {
            for (int i = 0; i < elements; i++)
            {
                for (int j = 0; j < elements; j++)
                {
                    pheromones[i, j] = (1-evaprate) * pheromones[i, j];
                }
            }
        }

        // --------------------------------------------------------------------------------------
        // Return a string of the pheromone map
        // --------------------------------------------------------------------------------------
        public string PheroMapAsString()
        {
            string map = "(";
            for (int i = 0; i < elements; i++)
            {
                for (int j = 0; j < elements; j++)
                {
                    map = map + this.pheromones[i, j].ToString() + " ";
                }
            }
            map = map + ")\n";
            return map;
        }



    }
}
